﻿using DevExpress.XtraEditors;
using DLL;
using NursingFormSystem;
using NursingFormSystem.Model;
using SQLDLL;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows.Forms;

namespace Common
{
    public class ElectronicSignature
    {
        #region 获取用户信息
        public static Tb_User LoginUser;
        public ElectronicSignature(Tb_User User)
        {
            //if (LoginUser == null)
            //{
            LoginUser = User;
            //}
        }
        #endregion

        #region 导入引用的接口
        [DllImport("PlatformLibrary.dll")]
        extern static byte RunUserLoginQrCodeOnCloudSignAndWaitUser();
        [DllImport("PlatformLibrary.dll")]
        extern static int SOF_CheckUsbKeyConnectedWithDeviceInfo(string deviceInfo);
        [DllImport("PlatformLibrary.dll")]
        extern static int SOF_LoginWithServerAddressAndDeviceInfo(string server, string deviceInfo, string password);
        [DllImport("PlatformLibrary.dll")]
        extern static int SOF_SignDataWithExtraInfo(string originData, string extraInfo, StringBuilder signData);
        [DllImport("PlatformLibrary.dll")]
        extern static byte GetSignCertOnUsbKey(StringBuilder signCert);
        [DllImport("PlatformLibrary.dll")]
        extern static int SOF_Close();
        [DllImport("PlatformLibrary.dll")]
        extern static int SOF_GenRandom(string server, int length, StringBuilder outData);
        [DllImport("PlatformLibrary.dll")]
        extern static int SOF_LoginAuthentication(string server, string signData, string cert, string originData);
        [DllImport("PlatformLibrary.dll")]
        extern static int SOF_VerifySign(string server, string signData, string cert, string originData);
        [DllImport("PlatformLibrary.dll")]
        extern static int GetSealImageBase64AndImageTypeByCloudSignUserIdCardNumFromServer(string server, string userIdCardNum, StringBuilder sealImageBase64, StringBuilder sealImageType);
        [DllImport("PlatformLibrary.dll")]
        extern static int GetSealImageBase64LengthByCloudSignUserIdCardNumFromServer(string server, string userIdCardNum);

        #endregion

        #region 通过身份证获取印章图片
        /// 重点内容：
        /// 电签内网版本的测试需先安装内网版本的云签客户端
        /// 电签外网版本的测试需先安装外网版本的云签客户端
        /// <param name="serverAddress">服务器地址</param>
        /// <param name="userIdCardNum">身份证</param>
        /// <param name="sealImageBase64">返回图片Base64字符串</param>
        /// <param name="sealImageType">返回图片类型</param>
        /// <param name="message">返回的消息</param>
        public void GetSealImageByCardNum(string serverAddress, string userIdCardNum, out string sealImageBase64, out string sealImageType, out string message)
        {
            sealImageBase64 = "";
            sealImageType = "";
            message = "";

            int iLength = GetSealImageBase64LengthByCloudSignUserIdCardNumFromServer(serverAddress, userIdCardNum);//获取图片长度
            if (iLength <= 0)
            {
                message = "获取印章图片长度失败";
                return;
            }
            StringBuilder sbSealImageBase64 = new StringBuilder(iLength);//先开辟内存空间
            StringBuilder sbSealImageType = new StringBuilder(64);
            int result = GetSealImageBase64AndImageTypeByCloudSignUserIdCardNumFromServer(serverAddress, userIdCardNum, sbSealImageBase64, sbSealImageType);
            if (result != 0)
            {
                message = "获取印章图片失败";
                return;
            }
            sealImageBase64 = sbSealImageBase64.ToString();
            sealImageType = sbSealImageType.ToString();
            string filePath = GetImgUrl(sealImageBase64, sealImageType, userIdCardNum) + "." + sealImageType;//获取图片保存路径
            string select_sql = string.Format("select count(Id) from P_Personnel(NOLOCK) where IdCardNo='{0}'", userIdCardNum);//根据身份证更新电签图片保存地址和Base64图片字符串
            DataSet data = SqlHelper.ExecuteDataset(select_sql, System.Data.CommandType.Text, Base.ConnNISStr, null);
            if (data != null && data.Tables != null && data.Tables.Count > 0)
            {
                string update_sql = string.Format("Update P_Personnel Set ElectricSignUrl='{0}', ElectricSignBase64 = '{1}' Where IdCardNo='{2}' AND IdCardNo is not null", filePath, sealImageBase64, userIdCardNum);
                SqlHelper.ExecuteNonQuery(Base.ConnNISStr, CommandType.Text, update_sql);
            }
            else
            {
                UpdateCardNo ucn = new UpdateCardNo(LoginUser);//呼出完善身份证窗口，进行身份证号的完善和印章图片的获取、印章图片的保存，以及更新印章图片路径、Base64图片字符串存到数据库表P_Personnel
                ucn.Show();
            }
            message = "获取印章图片成功";
            return;
        }
        #endregion

        #region 获取客户端Key签名
        /// <param name="serverAddress">云签服务器地址</param>
        /// <param name="deviceInfoSoft">软key值(身份证号)</param>
        /// <param name="originData">待签名原文(就是我们自己拼的xml)</param>
        /// <param name="extraInfo">取证扩展信息</param>
        /// <param name="signData">返回签名值(数据库必须开字段存)</param>
        /// <param name="certData">返回证书值(数据库必须开字段存)</param>
        public void GetSign(string serverAddress, string deviceInfoSoft, string originData, string extraInfo, out string signData, out string certData, out string message)
        {
            signData = "";
            certData = "";
            message = "";
            //仅软Key模式
            ModeOnlySoft(serverAddress, deviceInfoSoft, originData, extraInfo, out  signData, out  certData, out message);
            //还可以加其他操作模式
        }

        /// <param name="serverAddress">服务器地址</param>
        /// <param name="deviceInfoSoft">软key值(身份证号)</param>
        /// <param name="originData">待签名原文(就是我们自己拼的xml)</param>
        /// <param name="extraInfo">取证扩展信息</param>
        /// <param name="signData">返回签名值(数据库必须开字段存)</param>
        /// <param name="certData">返回证书值(数据库必须开字段存)</param>
        /// <param name="message">返回的消息</param>
        private void ModeOnlySoft(string serverAddress, string deviceInfoSoft, string originData, string extraInfo, out string signData, out string certData, out string message)
        {
            signData = "";
            certData = "";
            message = "";
            //判断Key是否插入
            if (SOF_CheckUsbKeyConnectedWithDeviceInfo(deviceInfoSoft) != 0)
            {
                //弹出软Key二维码界面，等待用户扫码
                if (RunUserLoginQrCodeOnCloudSignAndWaitUser() == 0)
                {
                    message = "失败，请插入自己的Key并输入正确的Key密码";
                    return;
                }
                else
                {
                    if (SOF_CheckUsbKeyConnectedWithDeviceInfo(deviceInfoSoft) != 0)
                    {
                        message = "失败，请插入自己的Key并输入正确的Key密码";
                        return;
                    }
                }
            }

            //打开Key并验证口令
            if (SOF_LoginWithServerAddressAndDeviceInfo(serverAddress, deviceInfoSoft, "") != 0)
            {
                message = "失败，请插入自己的Key并输入正确的Key密码";
                return;
            }

            //签名
            StringBuilder sbSignData = new StringBuilder(4096);
            if (SOF_SignDataWithExtraInfo(originData, extraInfo, sbSignData) != 0)
            {
                SOF_Close();
                message = "签名失败";
                return;
            }
            signData = sbSignData.ToString();
            //获取签名证书
            StringBuilder sbSignCert = new StringBuilder(4096);
            if (GetSignCertOnUsbKey(sbSignCert) == 0)
            {
                SOF_Close();
                message = "获取签名证书失败";
                return;
            }
            certData = sbSignCert.ToString();
            //关闭Key
            SOF_Close();
            message = "签名成功";
            return;
        }
        #endregion

        #region 连接服务器验证签名
        /// <param name="serverAddress">服务器地址</param>
        /// <param name="signData">签名值</param>
        /// <param name="certData">证书值</param>
        /// <param name="originData">待签名原文</param>
        /// <param name="message">返回的消息</param>
        /// <returns>验证签名成功：true，验证签名失败：false</returns>
        public bool VilidSign(string serverAddress, string signData, string certData, string originData, out string message)
        {
            bool flag = false;
            message = "";
            if (SOF_VerifySign(serverAddress, signData, certData, originData) != 0)
            {
                message = "验证签名失败";
            }
            else
            {
                flag = true;
                message = "验证签名成功";
            }
            return flag;
        }
        #endregion

        #region Base字符串还原图片
        /// <param name="sealImageBase64">Base64图片字符串</param>
        /// <param name="sealImageType">图片类型(只有GIF、PNG)</param>
        /// <param name="userIdCardNum">身份证</param>
        /// <returns></returns>
        public string GetImgUrl(string sealImageBase64, string sealImageType, string userIdCardNum)
        {
            string filePath = "";
            string fileDir = System.Windows.Forms.Application.StartupPath + "\\ElectronicSignatureFile\\";//文件目录
            string fileName = userIdCardNum;//文件名称
            filePath = Path.Combine(fileDir, fileName);//保存文件所在站点位置
            byte[] arr = Convert.FromBase64String(sealImageBase64);//将Base64String转为图片并保存            
            using (MemoryStream ms = new MemoryStream(arr))
            {
                System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);
                ////只有把当前的图像复制一份，然后把旧的Dispose掉，那个文件就不被锁住了，
                ////这样就可以放心覆盖原始文件，否则GDI+一般性错误(A generic error occurred in GDI+)
                if (sealImageType.ToLower() == "gif")
                {
                    try
                    {
                        bmp.Save(filePath + ".gif", System.Drawing.Imaging.ImageFormat.Gif);
                    }
                    catch 
                    {

                    }
                }
                if (sealImageType.ToLower() == "png")
                {
                    try
                    {
                        bmp.Save(filePath + ".png", System.Drawing.Imaging.ImageFormat.Png);
                    }
                    catch 
                    {

                    }
                }
                bmp.Dispose();
            }
            return filePath;
        }
        #endregion

        #region 根据工号获取身份证号、印章图片地址
        /// <param name="UserId">工号</param>
        /// <param name="ElectricSignUrl">印章图片地址</param>
        /// <returns></returns>
        public string GetCardNo(string UserId, out string ElectricSignUrl)
        {
            string cardNo = "";
            ElectricSignUrl = "";
            string sql = string.Format("Select IdCardNo,ElectricSignUrl from P_Personnel(NOLOCK) Where UserId='{0}'", UserId);
            DataTable dt = SqlHelper.ExecuteDataset(sql, CommandType.Text, Base.ConnNISStr, null).Tables[0];
            if (dt != null && dt.Rows.Count > 0)
            {
                cardNo = dt.Rows[0][0] + "";
                ElectricSignUrl = dt.Rows[0]["ElectricSignUrl"] + "";
            }

            return cardNo;
        }
        #endregion

        #region 根据登录用户ID 完成电签图片显示
        /// <summary>
        /// 根据登录用户ID 完成电签图片显示
        /// </summary>
        /// <param name="departmentCode">科室</param>
        /// <param name="userId">用户ID</param>
        /// <param name="picbox">电签图片对象</param>
        /// <param name="p">图片定点位置</param>
        /// <param name="txtNurse">被图片取代的文本框对象</param>
        public void SetSiginPic(string departmentCode, string userId, PictureBox picbox, Point p, TextEdit txtNurse)
        {
            if (string.IsNullOrEmpty(departmentCode)) return;
            if (string.IsNullOrEmpty(userId)) return;
            if (picbox == null) return;
            if (p == null) return;
            if (txtNurse == null) return;
            if (!Base.ESSwitch.Equals("on")) return;
            //获取电签 IdCardNo,ElectricSignBase64
            string sql = string.Format("SELECT IdCardNo,ElectricSignBase64 FROM P_Personnel(NOLOCK) WHERE DepartmentCode='{0}' AND UserId='{1}'", departmentCode, userId);
            DataSet ds = SqlHelper.ExecuteDataset(sql, CommandType.Text, Base.ConnNISStr, null);
            if (ds == null || ds.Tables[0].Rows.Count <= 0) return;
            DataTable dt = ds.Tables[0];
            DataRow row = dt.Rows[0];
            object IdCardNo = row["IdCardNo"];
            object ElectricSignBase64 = row["ElectricSignBase64"];
            if (IdCardNo == DBNull.Value || IdCardNo == null || string.IsNullOrEmpty(IdCardNo.ToString())) return;
            if (ElectricSignBase64 == DBNull.Value || ElectricSignBase64 == null || string.IsNullOrEmpty(ElectricSignBase64.ToString())) return;
            string fileUrl = GetImgUrl(ElectricSignBase64.ToString(), "png", IdCardNo.ToString()) + ".png";
            if (string.IsNullOrEmpty(fileUrl)) return;
            if (!File.Exists(fileUrl)) return;
            txtNurse.Visible = false;
            picbox.Visible = true;
            picbox.Location = p;
            picbox.Image = Image.FromFile(@fileUrl);
        }
        #endregion

        #region 初始化加载印章图片
        /// <param name="esSwitch">电签开关(Resources/NursingFormSystem.config文件里面)</param>
        /// <param name="LoginUser">登陆人Tb_User对象</param>
        /// <param name="txtNurse">护士签名文本框控件</param>
        /// <param name="picElectronicSignature">护士签名电签图片控件</param>
        /// <param name="p">控件位置对象</param>
        public void SearchElectronicSignatureImage(string esSwitch, Tb_User LoginUser, TextEdit txtNurse, PictureBox picElectronicSignature, Point p)
        {
            if (esSwitch == "on")
            {
                ElectronicSignature es = new ElectronicSignature(LoginUser);
                string ElectricSignUrl = "";//印章图片地址(此处暂时未用到，窗体加载时候获取印章图片时有用)
                string deviceInfoSoft = es.GetCardNo(LoginUser.UserId, out ElectricSignUrl);
                if (!string.IsNullOrEmpty(ElectricSignUrl))
                {
                    txtNurse.Visible = false;
                    picElectronicSignature.Visible = true;
                    picElectronicSignature.Location = p;
                    if (File.Exists(ElectricSignUrl))//判断文件是否存在
                    {
                        picElectronicSignature.Image = Image.FromFile(@ElectricSignUrl);
                    }
                    else
                    {
                        UpdateCardNo ucn = new UpdateCardNo(LoginUser);//呼出完善身份证窗口，进行身份证号的完善和印章图片的获取、印章图片的保存，以及更新印章图片路径、Base64图片字符串存到数据库表P_Personnel
                        ucn.ShowDialog();
                    }
                }
            }
            else
            {
                txtNurse.Visible = true;
                picElectronicSignature.Visible = false;
            }
        }
        #endregion

        #region 获取印章图片
        /// <param name="signXml">待签名原文xml</param>
        /// <param name="extraInfo">取证扩展信息</param>
        /// <param name="formID">表单ID</param>
        /// <param name="formName">表单名</param>
        /// <param name="PatientId">患者ID</param>
        /// <param name="VisitId">住院次数</param>
        public bool GetElectronicSignatureImage(ElectronicSignature es, string signXml, string extraInfo, string formID, string formName, string PatientId, int VisitId)
        {
            bool flag = false;
            string ElectricSignUrl = "";//印章图片地址(此处暂时未用到，窗体加载时候获取印章图片时有用)
            string deviceInfoSoft = es.GetCardNo(LoginUser.UserId, out ElectricSignUrl);
            if (!string.IsNullOrEmpty(deviceInfoSoft) && !string.IsNullOrEmpty(ElectricSignUrl))
            {
                string signData = "";
                string certData = "";
                string message = "";
                es.GetSign(Base.ESIP, deviceInfoSoft, signXml, extraInfo, out signData, out certData, out message);//获取签名
                bool IsSuccess = es.VilidSign(Base.ESIP, signData, certData, signXml, out message);//验证签名
                if (IsSuccess)
                {//签名成功
                    if (!string.IsNullOrEmpty(formID) && !string.IsNullOrEmpty(formName) && !string.IsNullOrEmpty(signData) && !string.IsNullOrEmpty(certData))
                    {
                        string select_sql = string.Format("Select * from [BNNursingDocsSystem_Develop].[dbo].[Electronic_Signature](NOLOCK) where FormID={0} and PatientId='{1}' and VisitId={2} and UserId='{3}'", int.Parse(formID), PatientId, VisitId, LoginUser.UserId);
                        DataTable select_dt = SqlHelper.ExecuteDataset(select_sql, CommandType.Text, Base.ConnStr, null).Tables[0];
                        if (select_dt != null && select_dt.Rows.Count > 0)//更新
                        {
                            string update_sql = string.Format("Update [BNNursingDocsSystem_Develop].[dbo].[Electronic_Signature] set SignData='{0}',CertData='{1}' where FormID={2} and PatientId='{3}' and VisitId={4} and UserId='{5}'", signData, certData, int.Parse(formID), PatientId, VisitId, LoginUser.UserId);
                            int update_result = SqlHelper.ExecuteNonQuery(Base.ConnStr, CommandType.Text, update_sql, null);
                            flag = update_result > 0 ? true : false;
                        }
                        else
                        {//新增
                            string sql = string.Format(@"INSERT INTO [BNNursingDocsSystem_Develop].[dbo].[Electronic_Signature]
                                                           ([UserId]
                                                           ,[FormName]
                                                           ,[FormID]
                                                           ,[SignData]
                                                           ,[CertData]
                                                           ,[PatientId]
                                                           ,[VisitId])
                                                     VALUES
                                                           ('{0}'
                                                            ,'{1}'
                                                            ,{2}
                                                            ,'{3}'
                                                            ,'{4}'
                                                            ,'{5}'
                                                            ,{6})", LoginUser.UserId, formName, int.Parse(formID), signData, certData, PatientId, VisitId);
                            int insert_result = SqlHelper.ExecuteNonQuery(Base.ConnStr, CommandType.Text, sql, null);
                            flag = insert_result > 0 ? true : false;
                        }
                    }
                }
            }
            else
            {
                flag = false;
                UpdateCardNo ucn = new UpdateCardNo(LoginUser);//呼出完善身份证窗口，进行身份证号的完善和印章图片的获取、印章图片的保存，以及更新印章图片路径、Base64图片字符串存到数据库表P_Personnel
                ucn.ShowDialog();
            }
            return flag;
        }
        #endregion

        #region 解析电签图片
        public string GetElectricSignBase64(string DepartmentCode, string UserName)
        {
            string image = string.Empty;
            string esSwitch = Base.ESSwitch;
            if (!esSwitch.Equals("on")) return image;
            string sql = string.Format("SELECT * FROM P_Personnel(NOLOCK) WHERE DepartmentCode = '{0}' AND name='{1}'", DepartmentCode, UserName);
            DataSet ds = DLL.SqlHelper.ExecuteDataset(sql, CommandType.Text, SQLDLL.Base.ConnNISStr, null);
            if (ds == null || ds.Tables.Count <= 0) return image;
            DataTable dt = ds.Tables[0];
            if (dt.Rows.Count <= 0) return image;
            if (dt.Rows[0]["ElectricSignBase64"] == DBNull.Value) return image;
            image = dt.Rows[0]["ElectricSignBase64"].ToString();
            return image;
        }
        #endregion

    }
}